/************************
 * 
 * @author Justin Adams 
 * Student # : 200735090
 * 
 ***********************/
package tttCore;


/** GameManager *****************************************************************
 * 
 * A class to handle all the the tictactoe related operations for the servlet
 * This class managers coordination betweent Player Vs. AI games
 *  
 * ****************************************************************************/
public class GameManager {
	// current game board being processed
	private Board gameBoard;
	// current AI in use
	private AI gameAI;
	// current piece for the place
	private char playerPiece;
	// current game status
	private GameStatus currentStatus = GameStatus.IN_PROGRESS;

	public GameManager(String input){
		// create a sequence of chars for the board from the input string
		CharSequence boardSequence = input.subSequence(0, 9);
		// get the AI type
		char aiType = input.charAt(input.length()-2);
		// get the ai piece
		char aiPiece = input.charAt(input.length()-1);
		// create a board from the board sequence
		String boardString = boardSequence.toString();
		createBoardFromString(boardString);
		// create the aI
		createAI(aiType, aiPiece);

	}

	// a method to thats calls the boards constructor to be created from a string
	private void createBoardFromString(String boardString){
		gameBoard = new Board(boardString);
	}

	//a mathod that calls the appropriate AI constructor (Easy or Hard) and assign its piece
	private void createAI(char type, char piece){
		boolean aiIsX;
		if(piece == 'X')
			aiIsX = true;
		else
			aiIsX = false;

		if(type == 'E')
			gameAI = new EasyAI(aiIsX);
		else
			gameAI = new HardAI(aiIsX);

	}

	//main function for game manager. checks to see if the client has won
	//if the client has not won, the AI can perform a move
	public void gameProcess(){
		checkForPlayerWin();
		// if the player didnt win
		if(currentStatus == GameStatus.IN_PROGRESS){
			// make an AI Move
			Move retMove = gameAI.chooseMove(gameBoard);
			// update the board
			gameBoard = new Board(gameBoard, retMove);
			//update the game status
			updateGameStatus(retMove);
		}
	}

	//function to check to see if the player has won
	private void checkForPlayerWin(){
		// find out which piece the player is based on the AI piece
		if(gameAI.getPiece() == 'X')
			playerPiece = 'O';
		else
			playerPiece = 'X';
		// test all winning conditions
		for (int r = 0; r < gameBoard.board.length; r++){
			testRowWin(playerPiece, r);
			testColWin(playerPiece, r);
		}
		testDiagWin(playerPiece);
		testAntiDiagWin(playerPiece);
		testDraw();
	}

	//this function checks to see if the AI has won based on the AI's Move
	private void updateGameStatus(Move retMove){
		// calls all win condition checks for the AI
		testRowWin(retMove.getPiece(), retMove.getI());
		testColWin(retMove.getPiece(), retMove.getJ());
		testDiagWin(retMove.getPiece());
		testAntiDiagWin(retMove.getPiece());
		testDraw();
	}

	// returns the current status of the game
	private GameStatus getCurrentStatus(){
		return currentStatus;
	}

	// calls the game board to out as string
	private String boardToString(){
		return gameBoard.printBoardAsString();
	}

	// piece together the response string for the client
	// 
	public String getResponseString(){
		return boardToString() + ";" + getCurrentStatus();
	}

	//** Private methods used to test winning conditions **/

	/** testDraw *****************************************************************
	 * 
	 * Calls the isFull method to see if the board is full, in which case currentStatus
	 * becomes draw.
	 * @pre		-	
	 * @params	- 
	 * @returns - 
	 * @post	- currentStatus is set to DRAW if the gameboard is full.
	 * ****************************************************************************/
	private void testDraw(){
		if(currentStatus == GameStatus.IN_PROGRESS)
			if(gameBoard.isFull())
				currentStatus = GameStatus.DRAW;
	}

	/** testRowWin *****************************************************************
	 * 
	 * Iterates through the rows to see if there is 3 of the same game piece in a row.
	 * Called from placePlayerPiece or aiPiece, uses the row index provided in each.
	 * @pre		-	
	 * @params	- c: Character to check for (X or O)
	 * 			  i: The row index of calling function's move
	 * @returns - 
	 * @post	- CurrentStatus is updated as X_WON or O_WON depending on the char,
	 * 			  if said event has occurred.
	 * ****************************************************************************/
	private void testRowWin(char c, int i){
		if(currentStatus == GameStatus.IN_PROGRESS)	//Only need to check if in progress. Prevents updates if win already occurred.
			for (int j = 0; j < 3; j++){	//Iterate through 3 col positions.
				if(gameBoard.get(i, j) != c)	//We're checking for a single char 3 times, 
					break;					//so if one col doesn't contain it, why continue?
				if (j == 2)	//3 in a row
					if(c == 'X')
						currentStatus = GameStatus.X_WON;
					else
						currentStatus = GameStatus.O_WON;
			}
	}

	/** testColWin *****************************************************************
	 * 
	 * Iterates through the cols to see if there is 3 of the same game piece in a col.
	 * Called from placePlayerPiece or aiPiece, uses the col index provided in each.
	 * @pre		-	
	 * @params	- c: Character to check for (X or O)
	 * 			  j: The col index of calling function's move
	 * @returns - 
	 * @post	- CurrentStatus is updated as X_WON or O_WON depending on the char,
	 * 			  if said event has occurred.
	 * ****************************************************************************/
	private void testColWin(char c, int j){
		if(currentStatus == GameStatus.IN_PROGRESS)
			for (int i = 0; i < 3; i++){
				if(gameBoard.get(i, j) != c)	//Same as testRowWin, except i is changing not j
					break;
				if (i == 2)
					if(c == 'X')
						currentStatus = GameStatus.X_WON;
					else
						currentStatus = GameStatus.O_WON;
			}
	}

	/** testDiagWin *****************************************************************
	 * 
	 * Iterates through the main diagnol (top left to bottom right) to check for 3
	 * of the same piece.
	 * 
	 * @pre		-	
	 * @params	- c: Character to check for (X or O)
	 * @returns - 
	 * @post	- CurrentStatus is updated as X_WON or O_WON depending on the char,
	 * 			  if said event has occurred.
	 * ****************************************************************************/
	private void testDiagWin(char c){
		if(currentStatus == GameStatus.IN_PROGRESS)
			for (int i = 0; i < 3; i++){
				if(gameBoard.get(i, i) != c)	//Main diag has coordinates 00, 11, 22
					break;
				if (i == 2)
					if(c == 'X')
						currentStatus = GameStatus.X_WON;
					else
						currentStatus = GameStatus.O_WON;
			}
	}

	/** testAntiDiagWin *****************************************************************
	 * 
	 * Checks the anti-diagnol (top right to bottom left) for 3 of the same char.
	 * @pre		-	
	 * @params	- c: Character to check for (X or O)
	 * @returns - 
	 * @post	- CurrentStatus is updated as X_WON or O_WON depending on the char,
	 * 			  if said event has occurred.
	 * ****************************************************************************/
	private void testAntiDiagWin(char c){
		if(currentStatus == GameStatus.IN_PROGRESS)
			for (int i = 0; i < 3; i++){
				if(gameBoard.get(i, 2-i) != c)	//Anti-diag coordinates are 02 01 00;
					break;
				if (i == 2)
					if(c == 'X')
						currentStatus = GameStatus.X_WON;
					else
						currentStatus = GameStatus.O_WON;
			}

	}


}
